#include "ListaOrdenada.h"

void LSO_Crear(TListaSimpleOrdenada *pLs, int TamanioDato,tfcreate fcreate,tfcomp fcomp,tfcopy fcopy,tfdestroy fdestroy,tforder forder)
{
	pLs->Corriente = NULL;
	pLs->Primero = NULL;
	pLs->TamanioDato = TamanioDato;
	pLs->fcreate = fcreate;
	pLs->fcomp = fcomp;
	pLs->fcopy = fcopy;
	pLs->fdestroy = fdestroy;
	pLs->forder = forder;
}

void LSO_Vaciar(TListaSimpleOrdenada *pLs)
{
	TNodoListaSimpleOrdenada *pNodo, *Siguiente;
	for(pNodo = pLs->Primero;(pNodo);pNodo=Siguiente)
	{
		Siguiente = pNodo->Siguiente;
		if (pLs->fdestroy)
			pLs->fdestroy(pNodo->Elem);
		free(pNodo->Elem);
		free(pNodo);
	}
	pLs->Primero=pLs->Corriente=NULL;
}

int LSO_Vacia(TListaSimpleOrdenada Ls)
{
	return (Ls.Primero == NULL);
}

void LSO_Elem_Cte(TListaSimpleOrdenada Ls, void *pE)
{
	if (Ls.fcopy)
		Ls.fcopy(pE,Ls.Corriente->Elem);
	else
		memcpy(pE, Ls.Corriente->Elem, Ls.TamanioDato);
}

int LSO_Mover_Cte(TListaSimpleOrdenada *pLs, TMovimiento_Lso M)
{
	switch (M)
	{
		case LSO_Primero:
			pLs->Corriente=pLs->Primero;
		break;
		case LSO_Siguiente:
			if (pLs->Corriente->Siguiente==NULL)
				return FALSE;
			else
				pLs->Corriente=pLs->Corriente->Siguiente;
		break;
		case LSO_Anterior:return FALSE;
	}
	return TRUE;
}

void LSO_Borrar_Cte(TListaSimpleOrdenada *pLs)
{
	TNodoListaSimpleOrdenada *pNodo=pLs->Corriente;
	if (pLs->Corriente==pLs->Primero)
	{
		pLs->Primero = pLs->Corriente->Siguiente;
		pLs->Corriente = pLs->Primero;
	}
	else
	{
		if(pLs->Corriente->Siguiente){
			/* En este caso en que el corriente no es el ultimo, puedo evitarme
			 * recorrer toda la lista buscando el anterior */
			pNodo=pLs->Corriente->Siguiente;

			if (pLs->fcopy)
				pLs->fcopy(pLs->Corriente->Elem,pNodo->Elem);
			else
				memcpy(pLs->Corriente->Elem, pNodo->Elem, pLs->TamanioDato);

			pLs->Corriente->Siguiente = pNodo->Siguiente;
		}else {
			TNodoListaSimpleOrdenada *pAux=pLs->Primero;
			while (pAux->Siguiente!=pLs->Corriente)
				pAux = pAux->Siguiente;
			pAux->Siguiente=pLs->Corriente->Siguiente;
			pLs->Corriente = pAux; /*Si es el �ltimo queda en el Anterior al borrado */
		}
	}
	if (pLs->fdestroy)
		pLs->fdestroy(pNodo->Elem);
	free(pNodo->Elem);
	free(pNodo);
}

int LSO_Insertar_Cte(TListaSimpleOrdenada *pLs, TMovimiento_Lso M, void* pE)
{
	TNodoListaSimpleOrdenada *pNodo = (TNodoListaSimpleOrdenada*) malloc(sizeof(TNodoListaSimpleOrdenada));
	if (!pNodo)
		return FALSE; /* No hay memoria disponible */
	pNodo->Elem = malloc (pLs->TamanioDato);
	if(!pNodo->Elem)
	{
		free(pNodo);
		return FALSE;
	}
	if (pLs->fcopy)
		pLs->fcopy(pNodo->Elem,pE);
	else
		memcpy(pNodo->Elem, pE, pLs->TamanioDato);
	if ((pLs->Primero == NULL) || (M==LSO_Primero) || ((M==LSO_Anterior) && (pLs->Primero==pLs->Corriente)))
	{
		/*Si est� vac�a o hay que insertar en el Primero o
		hay que insertar en el Anterior y el actual es el Primero */
		pNodo->Siguiente = pLs->Primero;
		pLs->Primero = pLs->Corriente = pNodo;
	}
	else
	{
		/* Siempre inserto como siguiente, el nodo nuevo, porque es más fácil */
		pNodo->Siguiente = pLs->Corriente->Siguiente;
		pLs->Corriente->Siguiente = pNodo;
		if (M == LSO_Anterior)
		{
			/* Pero cuando el movimiento es Anterior, entonces swapeo los
			 * elementos */
			void* tmp = pNodo->Elem;
			pNodo->Elem = pLs->Corriente->Elem;
			pLs->Corriente->Elem = tmp;
		}
	}
	pLs->Corriente=pNodo;
	return TRUE;
}

void LSO_Modificar_Cte(TListaSimpleOrdenada Ls, void *pE)
{
	if(Ls.fcopy)
		Ls.fcopy(Ls.Corriente->Elem,pE);
	else
		memcpy(Ls.Corriente->Elem, pE, Ls.TamanioDato);
}


int fCopyListaOrdenada(void *pTo,void *pFrom)
{
	TListaSimpleOrdenada *to = (TListaSimpleOrdenada*)pTo;
	TListaSimpleOrdenada *from = (TListaSimpleOrdenada*)pFrom;

	void *item;

	LSO_Crear(to,from->TamanioDato,from->fcreate, from->fcomp,from->fcopy,from->fdestroy,from->forder);

	if(!LSO_Vacia(*(from)))
	{


		LSO_Mover_Cte(from,LSO_Primero);

		do
		{
			item = malloc(from->TamanioDato);
			LSO_Elem_Cte(*(from),item);


			LSO_Insertar_Cte(to,LSO_Siguiente,item);


			if(from->fdestroy)
				from->fdestroy(item);
			free(item);
			item = NULL;

		}while(LSO_Mover_Cte(from,LSO_Siguiente));

	}

	return EXIT_SUCCESS;
}




int LSO_EliminarElemento(TListaSimpleOrdenada *pLso,void *elem)
{
	void *item;
	int encontrado = FALSE;

	if (!LSO_Vacia(*(pLso)))
	{
		LSO_Mover_Cte(pLso,LSO_Primero);

		do
		{
			item = malloc(pLso->TamanioDato);
			if(pLso->fcreate)
				pLso->fcreate(item);

			LSO_Elem_Cte(*(pLso),item);

			if (pLso->fcomp(item,elem)==0)
			{
				encontrado = TRUE;
				LSO_Borrar_Cte(pLso);
			}
			if (pLso->fdestroy)
				pLso->fdestroy(item);
			free(item);

		}
		while(LSO_Mover_Cte(pLso,LSO_Siguiente) && (encontrado == FALSE));
	}
	return EXIT_SUCCESS;
}

int LSO_ModificarElemento(TListaSimpleOrdenada *pLso,void *elem)
{
	void *item;
	int encontrado = FALSE;

	if (!LSO_Vacia(*(pLso)))
	{
		LSO_Mover_Cte(pLso,LSO_Primero);

		do
		{
			item = malloc(pLso->TamanioDato);
			if(pLso->fcreate)
				pLso->fcreate(item);

			LSO_Elem_Cte(*(pLso),item);

			if (pLso->fcomp(item,elem)==0)
			{
				encontrado = TRUE;
				LSO_Modificar_Cte(*(pLso),elem);
			}
			if (pLso->fdestroy)
				pLso->fdestroy(item);
			free(item);

		}
		while(LSO_Mover_Cte(pLso,LSO_Siguiente) && (encontrado == FALSE));
	}
	return EXIT_SUCCESS;
}

int LSO_ConsultarElemento(TListaSimpleOrdenada *pLso,void *elem)
{
	void *item;
	int encontrado = FALSE;

	if (!LSO_Vacia(*(pLso)))
	{
		LSO_Mover_Cte(pLso,LSO_Primero);

		do
		{
			item = malloc(pLso->TamanioDato);
			if(pLso->fcreate)
				pLso->fcreate(item);

			LSO_Elem_Cte(*(pLso),item);

			if (pLso->fcomp(item,elem)==0)
			{
				encontrado = TRUE;
				pLso->fcopy(elem,item);
			}
			if (pLso->fdestroy)
				pLso->fdestroy(item);
			free(item);

		}
		while(LSO_Mover_Cte(pLso,LSO_Siguiente) && (encontrado == FALSE));
	}
	return encontrado;
}


int LSO_AgregarElemento(TListaSimpleOrdenada *pLso,void *elem)
{
	void *item;
	int insertado = FALSE;

	if (!LSO_Vacia(*(pLso)))
	{
		LSO_Mover_Cte(pLso,LSO_Primero);

		do
		{
			item = malloc(pLso->TamanioDato);
			if(pLso->fcreate)
				pLso->fcreate(item);

			LSO_Elem_Cte(*(pLso),item);

			int cmp = pLso->forder(elem,item);

			if(cmp<0)
			{
				insertado = TRUE;
				LSO_Insertar_Cte(pLso, LSO_Anterior, elem);
			}

			/*
			if (cmp == 0)//ya existia
			{
				return EXIT_FAILURE;
			}
			*/

			if (pLso->fdestroy)
				pLso->fdestroy(item);
			free(item);

		}
		while(LSO_Mover_Cte(pLso,LSO_Siguiente) && (insertado == FALSE));

		if (insertado == FALSE)
			LSO_Insertar_Cte(pLso, LSO_Siguiente, elem);

	}
	else
	{
		LSO_Insertar_Cte(pLso, LSO_Siguiente, elem);
	}
	return EXIT_SUCCESS;
}

